NoiseMap = class()

function NoiseMap:init(seed,baseScale,octaveCount,persistance)
    -- seed -- or math.floor(math.random() * 1938527 - math.random() * 173777)
    self.seed = math.abs(seed * 7 + 123) % 200000
    self.xBase = (self.seed * 37 + 13) // 127 + 2048
    self.yBase = (self.seed * 14 + 31) // 67 + 512
    self.zBase = (self.seed * 31 + 7) // 103 + 2048
    self:setScale(baseScale,octaveCount,persistance)
end

function NoiseMap:setScale(baseScale,octaveCount,persistance)
    if baseScale and baseScale > 1 then baseScale = 1 end
    self.baseScale = baseScale or 0.2
    self.octaveCount = octaveCount or 4
    self.persistance = persistance or 0.25
end

function NoiseMap:genPerlinNoise2DArray(xBase,yBase,length,width)
    local smoothedNoise = {}
    for i = 1,self.octaveCount do
        smoothedNoise[i] = self:gen2DNoiseArray(xBase,yBase,length,width,i)
    end
    local perlinNoise = {}
    for i = 1, length * width do
        perlinNoise[i] = 0
    end

    local amplitude = 1.0
    local totalAmplitude = 0.0
    for octave = 1,self.octaveCount do
        amplitude = amplitude * self.persistance
        totalAmplitude = totalAmplitude + amplitude
        for i = 1, length * width do
            perlinNoise[i] = perlinNoise[i] + smoothedNoise[octave][i] * amplitude
        end
    end
    
    for i = 1, length * width do
        perlinNoise[i] = perlinNoise[i] / totalAmplitude
    end
    return perlinNoise
end

function NoiseMap:gen2DNoiseArray(xBase,yBase,length,width,octave)
    local noises = {}
    local xa,ya = self.xBase + xBase,self.yBase + yBase
    -- 平滑程度
    local noiseScale = self.baseScale * 2 ^ (octave - 1)
    local n = noise
    for x = 0, length - 1 do
        for y = 0, width - 1 do
            noises[x * width + y + 1] = (n((x + xa) * noiseScale, (y + ya) * noiseScale) + 1) / 2
        end
    end
    return noises
end

function NoiseMap:getPerlinNoise2D(x,y)
    local xa,ya = self.xBase + x,self.yBase + y
    -- 平滑程度
    local noiseScale
    local smoothedNoise = {}
    for i = 1,self.octaveCount do
        noiseScale = self.baseScale * 2 ^ (i - 1)
        smoothedNoise[i] = (noise(xa * noiseScale, ya * noiseScale) + 1) / 2
    end
    local perlinNoise = 0
    local amplitude = 1.0
    local totalAmplitude = 0.0
    for octave = 1,self.octaveCount do
        amplitude = amplitude * self.persistance
        totalAmplitude = totalAmplitude + amplitude
        perlinNoise = perlinNoise + smoothedNoise[octave] * amplitude
        
    end
    perlinNoise = perlinNoise / totalAmplitude
    return perlinNoise
end


function NoiseMap:genPerlinNoise2D(xBase,yBase,length,width)
    local smoothedNoise = {}
    for i = 1,self.octaveCount do
        smoothedNoise[i] = self:gen2DNoise(xBase,yBase,length,width,i)
    end
    local perlinNoise = {}
    for x = 1, length do
        perlinNoise[x] = {}
        for y = 1, width do
            perlinNoise[x][y] = 0
        end
    end

    local amplitude = 1.0
    local totalAmplitude = 0.0
    for octave = 1,self.octaveCount do
        amplitude = amplitude * self.persistance
        totalAmplitude = totalAmplitude + amplitude
        for x = 1, length do
            for y = 1, width do
                perlinNoise[x][y] = perlinNoise[x][y] + smoothedNoise[octave][x][y] * amplitude
            end
        end
        
    end
    
    for x = 1, length do
        for y = 1, width do
            perlinNoise[x][y] = perlinNoise[x][y] / totalAmplitude
        end
    end
    return perlinNoise
end

function NoiseMap:gen2DNoise(xBase,yBase,length,width,octave)
    local noises = {}
    local xa,ya = self.xBase + xBase,self.yBase + yBase
    -- 平滑程度
    local noiseScale = self.baseScale * 2 ^ (octave - 1)
    local n = noise
    for x = 1, length do
        noises[x] = {}
        for y = 1, width do
            noises[x][y] = (n((x + xa) * noiseScale, (y + ya) * noiseScale) + 1) / 2
        end
    end
    return noises
end

function NoiseMap:getPerlinNoise3DNoOctave(x,y,z)
    local xa,ya,za = self.xBase + x,self.yBase + y,self.zBase + z
    -- 平滑程度
    local noiseScale = self.baseScale
    return (noise(xa * noiseScale, ya * noiseScale, za * noiseScale) + 1) / 2
end

function NoiseMap:getPerlinNoise3D(x,y,z)
    local xa,ya,za = self.xBase + x,self.yBase + y,self.zBase + z
    -- 平滑程度
    local noiseScale
    local smoothedNoise = {}
    for i = 1,self.octaveCount do
        noiseScale = self.baseScale * 2 ^ (i - 1)
        smoothedNoise[i] = (noise(xa * noiseScale, ya * noiseScale, za * noiseScale) + 1) / 2
    end
    local perlinNoise = 0
    local amplitude = 1.0
    local totalAmplitude = 0.0
    for octave = 1,self.octaveCount do
        amplitude = amplitude * self.persistance
        totalAmplitude = totalAmplitude + amplitude
        perlinNoise = perlinNoise + smoothedNoise[octave] * amplitude
        
    end
    perlinNoise = perlinNoise / totalAmplitude
    return perlinNoise
end

function NoiseMap:genPerlinNoise3D(xBase,yBase,zBase,length,height,width)
    local smoothedNoise = {}
    for i = 1,self.octaveCount do
        smoothedNoise[i] = self:gen3DNoise(xBase,yBase,zBase,length,height,width,i)
    end
    local perlinNoise = {}
    for x = 1, length do
        perlinNoise[x] = {}
        for y = 1, height do
            perlinNoise[x][y] = {}
            for z = 1, width do
                perlinNoise[x][y][z] = 0
            end
        end
    end

    local amplitude = 1.0
    local totalAmplitude = 0.0
    for octave = 1,self.octaveCount do
        amplitude = amplitude * self.persistance
        totalAmplitude = totalAmplitude + amplitude
        for x = 1, length do
            for y = 1, height do
                for z = 1, width do
                    perlinNoise[x][y][z] = perlinNoise[x][y][z] + smoothedNoise[octave][x][y][z] * amplitude
                end
            end
        end
        
    end
    
    for x = 1, length do
        for y = 1, height do
            for z = 1, width do
                perlinNoise[x][y][z] = perlinNoise[x][y][z] / totalAmplitude
            end
        end
    end
    return perlinNoise
end

function NoiseMap:gen3DNoise(xBase,yBase,zBase,length,height,width,octave)
    local noises = {}
    local xa,ya,za = self.xBase + xBase,self.yBase + yBase,self.zBase + zBase
    -- 平滑程度
    local noiseScale = self.baseScale * 2 ^ (octave - 1)
    local n = noise
    for x = 1, length do
        noises[x] = {}
        for y = 1, height do
            noises[x][y] = {}
            for z = 1, width do
                noises[x][y][z] = (n((x + xa) * noiseScale, (y + ya) * noiseScale, (z + za) * noiseScale) + 1) / 2
            end
        end
    end
    return noises
end

-- 将二维数组转换到一维数组
function NoiseMap.convert2DMapToArray(map)
    local array = {}
    local length,width = #map,#map[1]
    for x = 1, length do
        for y = 1, width do
            array[(x - 1) * width + y] = map[x][y]
        end
    end
    return array
end

-- 噪声混合，alpha取值0～1，返回混合后的噪声
function NoiseMap.calcMix2D(map1,map2,alpha)
    local noises = {}
    local length,width = #map1,#map1[1]
    for x = 1, length do
        noises[x] = {}
        for y = 1, width do
            local a,b = map1[x][y],map2[x][y]
            noises[x][y] = a + (b - a) * alpha
        end
    end
end

-- 噪声叠加，将两个噪声相加，返回相加后的噪声
function NoiseMap.calcAdd2D(map1,map2)
    local noises = {}
    local length,width = #map1,#map1[1]
    for x = 1, length do
        noises[x] = {}
        for y = 1, width do
            noises[x][y] = map1[x][y] + map2[x][y]
        end
    end
    return noises
end

-- 噪声叠加，将两个噪声相乘，返回相乘后的噪声
function NoiseMap.calcMultiply2D(map1,map2)
    local noises = {}
    local length,width = #map1,#map1[1]
    for x = 1, length do
        noises[x] = {}
        for y = 1, width do
            noises[x][y] = map1[x][y] * map2[x][y]
        end
    end
    return noises
end

-- 噪声缩放，s是缩放比例
function NoiseMap.scale2D(map,s)
    local length,width = #map,#map[1]
    for x = 1, length do
        for y = 1, width do
            map[x][y] = map[x][y] * s
        end
    end
end

-- 分离噪声，极值化，用来生成山峰
function NoiseMap.peak2D(map,least)
    local length,width = #map,#map[1]
    for x = 1, length do
        for y = 1, width do
            if map[x][y] < least then
                map[x][y] = 0
            end
        end
    end
end

-- 噪声自增加
function NoiseMap.add2D(map,a)
    local length,width = #map,#map[1]
    for x = 1, length do
        for y = 1, width do
            map[x][y] = map[x][y] + a
        end
    end
end

-- 复制噪声
function NoiseMap.clone2D(map)
    local noises = {}
    local length,width = #map,#map[1]
    for x = 1, length do
        noises[x] = {}
        for y = 1, width do
            noises[x][y] = map[x][y]
        end
    end
    return noises
end

-- 噪声扩张，s为扩张系数
function NoiseMap.expend2D(map,s)
    local length,width = #map,#map[1]
    local c = 0.5
    for x = 1, length do
        for y = 1, width do
            map[x][y] = map[x][y] + (map[x][y] - c) * s
        end
    end
end

-- 带过滤的增加噪声
function NoiseMap.addWithFilter2D(map,a,filter)
    local length,width = #map,#map[1]
    for x = 1, length do
        for y = 1, width do
            if map[x][y] > filter then
                map[x][y] = map[x][y] + a
            end
        end
    end
end

-- 将噪声截取在某个范围(min,max)内
function NoiseMap.clamp2D(map,min,max)
    local length,width = #map,#map[1]
    for x = 1, length do
        for y = 1, width do
            if map[x][y] < min then
                map[x][y] = min
            elseif map[x][y] > max then
                map[x][y] = max
            end
        end
    end
end

-- 将噪声截取在标准范围内(0,1)
function NoiseMap.normalClamp2D(map)
    NoiseMap.clamp2D(map,0,1)
end
